/**
 * 输出的Token
 */
class Token {
    public type:number;
    public text:string;

    constructor(type:number,text:string){
        this.type = type;
        this.text = text;
    }

    toString(){
        return ''
    }
}

/**
 * 词法解析器公共类
 */
abstract class Lexer {
    static EOF:string = String.fromCharCode(-1);
    static EOF_TYPE = 1;
    
    input:string;
    p:number = 0;
    c:string;
    
    constructor(input:string){
        this.input = input;
        this.c = this.input.charAt(this.p);
    }

    /**
     * 移动到下一个字符
     */
    consume(){
        this.p = this.p + 1;
        if(this.p >= this.input.length){
            this.c = Lexer.EOF
        }else{
            this.c = this.input.charAt(this.p)
        }
    }

    /**
     * 匹配x
     * @param x 
     */
    match(x:string){
        if(this.c == x){
            this.consume()
        }else{
            throw new Error("expecting : " + x + "; found " + this.c);
        }
    }

    abstract nextToken():Token
    abstract getTokenName(tokenType:number):string;
}


/**
 * 特定场景的词法解析器
 */
class ListLexer extends Lexer {
    // 注册Token
    static NAME = 2;
    static COMMA = 3;
    static LBRACK = 4;
    static RBRACK = 5;

    static tokenNames:string[] = [
        'n/a',
        "<EOF>",
        "NAME",
        "COMMA",
        "LBRACK",
        "RBRACK"
    ]
    
    /**
     * 依次获取Token
     * @returns 
     */
    nextToken(): Token {
       while(this.c != Lexer.EOF){
            switch(this.c){
                case ' '  : 
                case '\t' : 
                case '\n' :
                case '\r' :
                    this.WS();
                    break;
                
                case ',' :
                    this.consume();
                    return new Token(ListLexer.COMMA,',')
                case '[':
                    this.consume();
                    return new Token(ListLexer.LBRACK,'[')
                case ']':
                    this.consume();
                    return new Token(ListLexer.RBRACK,']')
                default:
                    if(this.isLETTER()){
                        return this.Name()
                    }else{
                        throw new Error("invalid char: " + this.c)
                    }
            }
       }

       return new Token(ListLexer.EOF_TYPE,"<EOF>");
    }
    isLETTER(){
        return (this.c >= 'a' && this.c <= 'z')
        || (this.c >= 'A' && this.c <= 'Z')
    }
    Name(){
        let buf = '';
        do {
            buf = buf + this.c;
            this.consume();
        }while(this.isLETTER())

        return new Token(ListLexer.NAME,buf);
    }
    WS(){
        let whiteSpace = [' ','\t','\n','\r']
        while (
            whiteSpace.indexOf(this.c) > -1
        ){
            this.consume();
        }
    }

    getTokenName(tokenType: number): string {
        return ListLexer.tokenNames[tokenType];
    }

}


export {
    Token,
    Lexer,
    ListLexer
}